/**
 * Endpoint Helper Module
 * Provides common functionality for API endpoint interactions
 */

class EndpointHelper {
    constructor() {
        this.retryConfig = {
            maxRetries: 3,
            retryDelay: 1000,
            retryableStatuses: [408, 429, 500, 502, 503, 504]
        };

        this.timeoutConfig = {
            default: 30000,  // 30 seconds
            long: 120000,    // 2 minutes for long operations
            upload: 300000   // 5 minutes for file uploads
        };
    }

    /**
     * Create a standard endpoint section
     */
    createEndpointSection(config) {
        const {
            id,
            method = 'GET',
            path,
            description,
            fields = [],
            bodyType = 'none',
            hasFileUpload = false,
            timeout = 'default'
        } = config;

        const section = document.createElement('div');
        section.className = 'endpoint-section';
        section.id = `section_${id}`;

        // Build HTML
        let html = `
            <h2>
                <span class="endpoint-method ${method.toLowerCase()}">${method}</span>
                <span class="endpoint-path">${path}</span>
            </h2>
            ${description ? `<p>${description}</p>` : ''}
        `;

        // Add form fields
        fields.forEach(field => {
            html += this.createFormField(field, id);
        });

        // Add request + cURL buttons (no inline handlers)
        const buttonClass = method === 'DELETE' ? 'btn-danger' : '';
        html += `
            <button class="api-button ${buttonClass}"
                    data-action="exec" data-id="${id}" data-method="${method}" data-path="${path}" data-body="${bodyType}" data-timeout="${timeout}">
                ${this.getButtonText(method)}
            </button>
            <button class="btn btn-secondary" data-action="curl" data-id="${id}" data-method="${method}" data-path="${path}" data-body="${bodyType}" style="margin-left: 10px;">
                Show cURL
            </button>
            <pre id="${id}_response"></pre>
            <div id="${id}_correlation" class="correlation-snippet" aria-live="polite" style="margin-top:6px; color: var(--color-text-muted); font-size: 0.85em;"></div>
            <pre id="${id}_curl" style="display: none;"></pre>
        `;

        if (window.SafeDOM && typeof window.SafeDOM.setHTML === 'function') {
            window.SafeDOM.setHTML(section, html);
        } else {
            section.innerHTML = html;
        }
        // Bind actions
        try {
            const execBtn = section.querySelector('button[data-action="exec"]');
            if (execBtn && !execBtn._bound) {
                execBtn._bound = true;
                execBtn.addEventListener('click', (e) => {
                    e.preventDefault();
                    const m = execBtn.getAttribute('data-method');
                    if (m === 'DELETE') {
                        if (!confirm('Are you sure?')) return;
                    }
                    this.executeRequest(
                        execBtn.getAttribute('data-id'),
                        m,
                        execBtn.getAttribute('data-path'),
                        execBtn.getAttribute('data-body'),
                        execBtn.getAttribute('data-timeout') || 'default'
                    );
                });
            }
            const curlBtn = section.querySelector('button[data-action="curl"]');
            if (curlBtn && !curlBtn._bound) {
                curlBtn._bound = true;
                curlBtn.addEventListener('click', (e) => {
                    e.preventDefault();
                    this.showCurl(
                        curlBtn.getAttribute('data-id'),
                        curlBtn.getAttribute('data-method'),
                        curlBtn.getAttribute('data-path'),
                        curlBtn.getAttribute('data-body')
                    );
                });
            }
        } catch (_) {}
        return section;
    }

    /**
     * Create a form field based on type
     */
    createFormField(field, endpointId) {
        const {
            name,
            type = 'text',
            label,
            placeholder = '',
            required = false,
            defaultValue = '',
            options = [],
            description = ''
        } = field;

        const fieldId = `${endpointId}_${name}`;
        const requiredMark = required ? '<span class="required">*</span>' : '';

        let html = '<div class="form-group">';

        switch (type) {
            case 'textarea':
                html += `
                    <label for="${fieldId}">${label} ${requiredMark}:</label>
                    <textarea id="${fieldId}" rows="5" placeholder="${placeholder}">${defaultValue}</textarea>
                `;
                break;

            case 'select':
                html += `
                    <label for="${fieldId}">${label} ${requiredMark}:</label>
                    <select id="${fieldId}">
                        ${options.map(opt =>
                            `<option value="${opt.value}" ${opt.value === defaultValue ? 'selected' : ''}>
                                ${opt.label}
                            </option>`
                        ).join('')}
                    </select>
                `;
                break;

            case 'checkbox':
                html += `
                    <label>
                        <input type="checkbox" id="${fieldId}" ${defaultValue ? 'checked' : ''}>
                        ${label}
                    </label>
                `;
                break;

            case 'file':
                const accept = field.accept || '';
                const multiple = field.multiple ? 'multiple' : '';
                html += `
                    <label for="${fieldId}">${label} ${requiredMark}:</label>
                    <input type="file" id="${fieldId}" ${multiple} ${accept ? `accept="${accept}"` : ''}>
                `;
                break;

            case 'number':
                html += `
                    <label for="${fieldId}">${label} ${requiredMark}:</label>
                    <input type="number" id="${fieldId}"
                           value="${defaultValue}"
                           placeholder="${placeholder}"
                           ${field.min !== undefined ? `min="${field.min}"` : ''}
                           ${field.max !== undefined ? `max="${field.max}"` : ''}
                           ${field.step !== undefined ? `step="${field.step}"` : ''}>
                `;
                break;

            default: // text, password, etc.
                html += `
                    <label for="${fieldId}">${label} ${requiredMark}:</label>
                    <input type="${type}" id="${fieldId}"
                           value="${defaultValue}"
                           placeholder="${placeholder}">
                `;
        }

        if (description) {
            html += `<small>${description}</small>`;
        }

        html += '</div>';
        return html;
    }

    /**
     * Execute request with enhanced error handling and retry logic
     */
    async executeRequest(endpointId, method, path, bodyType = 'none', timeoutType = 'default') {
        const responseEl = document.getElementById(`${endpointId}_response`);
        if (!responseEl) {
            console.error(`Response element not found for ${endpointId}`);
            return;
        }

        try {
            Loading.show(responseEl.parentElement, 'Sending request...');
            responseEl.textContent = '';

            // Build the request
            const { body, query, processedPath } = this.buildRequest(endpointId, path, bodyType);

            // Set timeout
            const timeout = this.timeoutConfig[timeoutType] || this.timeoutConfig.default;

            // Execute with retry logic
            const response = await this.executeWithRetry(
                () => apiClient.makeRequest(method, processedPath, { body, query, timeout }),
                endpointId
            );

            // Display response
            this.displayResponse(responseEl, response, true);
            Toast.success('Request completed successfully');

        } catch (error) {
            this.displayError(responseEl, error);
            Toast.error(`Request failed: ${error.message}`);
        } finally {
            Loading.hide(responseEl.parentElement);
        }
    }

    /**
     * Execute request with retry logic
     */
    async executeWithRetry(requestFn, endpointId, attempt = 1) {
        try {
            return await requestFn();
        } catch (error) {
            const shouldRetry = this.shouldRetry(error, attempt);

            if (shouldRetry) {
                const delay = this.retryConfig.retryDelay * attempt;
                Toast.warning(`Request failed. Retrying in ${delay}ms... (Attempt ${attempt + 1}/${this.retryConfig.maxRetries})`);

                await this.delay(delay);
                return this.executeWithRetry(requestFn, endpointId, attempt + 1);
            }

            throw error;
        }
    }

    /**
     * Determine if request should be retried
     */
    shouldRetry(error, attempt) {
        if (attempt >= this.retryConfig.maxRetries) {
            return false;
        }

        // Check if error has a retryable status code
        const errorMessage = error.message || '';
        const statusMatch = errorMessage.match(/HTTP (\d+)/);

        if (statusMatch) {
            const status = parseInt(statusMatch[1]);
            return this.retryConfig.retryableStatuses.includes(status);
        }

        // Retry on network errors
        return errorMessage.includes('NetworkError') ||
               errorMessage.includes('Failed to fetch') ||
               errorMessage.includes('timeout');
    }

    /**
     * Build request body and query parameters
     */
    buildRequest(endpointId, path, bodyType) {
        let body = null;
        let query = {};
        let processedPath = path;

        // Process path parameters
        const pathParams = path.match(/\{([^}]+)\}/g);
        if (pathParams) {
            pathParams.forEach(param => {
                const paramName = param.slice(1, -1);
                const input = document.getElementById(`${endpointId}_${paramName}`);
                if (input && input.value) {
                    processedPath = processedPath.replace(param, input.value);
                }
            });
        }

        // Build body based on type
        if (bodyType === 'json' || bodyType === 'json_with_query') {
            const payloadEl = document.getElementById(`${endpointId}_payload`);
            if (payloadEl) {
                try {
                    body = JSON.parse(payloadEl.value);
                } catch (e) {
                    throw new Error(`Invalid JSON: ${e.message}`);
                }
            } else {
                // Build from form fields
                body = this.buildJsonFromFields(endpointId);
            }
        }

        if (bodyType === 'form') {
            body = this.buildFormData(endpointId);
        }

        if (bodyType === 'query' || bodyType === 'json_with_query') {
            query = this.buildQueryParams(endpointId);
        }

        return { body, query, processedPath };
    }

    /**
     * Build JSON from form fields
     */
    buildJsonFromFields(endpointId) {
        const data = {};
        // Support both patterns: "section_<id>" (preferred) and legacy "<id>"
        let section = document.getElementById(`section_${endpointId}`);
        if (!section) {
            section = document.getElementById(endpointId);
        }

        if (!section) return data;

        // Find all inputs in this section
        const inputs = section.querySelectorAll('input, textarea, select');
        inputs.forEach(input => {
            const name = input.id.replace(`${endpointId}_`, '');

            // Skip path parameters and special fields
            if (name.includes('response') || name.includes('curl') || name.includes('payload')) {
                return;
            }

            if (input.type === 'checkbox') {
                data[name] = input.checked;
            } else if (input.type === 'number') {
                if (input.value) data[name] = parseFloat(input.value);
            } else if (input.value) {
                data[name] = input.value;
            }
        });

        return data;
    }

    /**
     * Build FormData from form fields
     */
    buildFormData(endpointId) {
        const formData = new FormData();
        let section = document.getElementById(`section_${endpointId}`);
        if (!section) {
            section = document.getElementById(endpointId);
        }

        if (!section) return formData;

        const inputs = section.querySelectorAll('input, textarea, select');
        inputs.forEach(input => {
            const name = input.id.replace(`${endpointId}_`, '');

            if (input.type === 'file' && input.files.length > 0) {
                Array.from(input.files).forEach(file => {
                    formData.append(name, file);
                });
            } else if (input.type === 'checkbox') {
                formData.append(name, input.checked);
            } else if (input.value) {
                formData.append(name, input.value);
            }
        });

        return formData;
    }

    /**
     * Build query parameters
     */
    buildQueryParams(endpointId) {
        const params = {};
        let section = document.getElementById(`section_${endpointId}`);
        if (!section) {
            section = document.getElementById(endpointId);
        }
        if (!section) return params;

        // Look for fields marked as query parameters
        const queryInputs = section.querySelectorAll('[data-query="true"]');
        queryInputs.forEach(input => {
            const name = input.id.replace(`${endpointId}_`, '');
            if (input.value) {
                params[name] = input.value;
            }
        });

        return params;
    }

    /**
     * Display response with enhanced formatting
     */
    displayResponse(element, response, success = true) {
        if (typeof response === 'object') {
            // Use JSON viewer for objects
            const viewer = new JSONViewer(element, response, {
                expanded: 2,
                theme: document.documentElement.getAttribute('data-theme') || 'light',
                enableCopy: true,
                enableCollapse: true
            });
        } else {
            element.textContent = response;
        }

        // Add success/error styling
        element.className = success ? 'response-success' : 'response-error';

        // Update correlation snippet if present
        try {
            this.updateCorrelationSnippet(element);
        } catch (e) { /* ignore */ }
    }

    /**
     * Display detailed error information
     */
    displayError(element, error) {
        const errorInfo = {
            message: error.message,
            timestamp: new Date().toISOString(),
            details: {}
        };

        // Extract additional error information
        if (error.response) {
            errorInfo.details.status = error.response.status;
            errorInfo.details.statusText = error.response.statusText;
            errorInfo.details.headers = error.response.headers;
        }

        if (error.stack) {
            errorInfo.stack = error.stack.split('\n').slice(0, 5).join('\n');
        }

        this.displayResponse(element, errorInfo, false);
    }

    /**
     * Update correlation snippet next to the given response element.
     */
    updateCorrelationSnippet(responseEl) {
        if (!responseEl) return;
        // Derive endpoint id from responseEl.id if possible
        const id = (responseEl.id || '').replace(/_response$/, '');
        const corrId = id ? `${id}_correlation` : '';
        let box = (corrId && document.getElementById(corrId)) || null;
        if (!box) {
            // Create after response element if not found
            box = document.createElement('div');
            box.className = 'correlation-snippet';
            box.style.marginTop = '6px';
            box.style.color = 'var(--color-text-muted)';
            box.style.fontSize = '0.85em';
            try { box.setAttribute('aria-live', 'polite'); } catch(_){}
            responseEl.parentNode.insertBefore(box, responseEl.nextSibling);
        }
        const meta = (window.apiClient && window.apiClient.lastCorrelation) || {};
        const reqId = meta.requestId || '-';
        const trace = meta.traceparent || meta.traceId || '-';
        const shortReq = String(reqId).length > 12 ? `${String(reqId).slice(0, 12)}…` : reqId;
        const shortTr = String(trace).length > 24 ? `${String(trace).slice(0, 24)}…` : trace;
        box.textContent = `Correlation: X-Request-ID=${shortReq}  trace=${shortTr}`;
        box.title = `X-Request-ID=${reqId}  traceparent/X-Trace-Id=${trace}`;

        // Tiny help hint explaining how to use curl -I with X-Request-ID
        const section = responseEl.closest('.endpoint-section');
        const pathEl = section ? section.querySelector('.endpoint-path') : null;
        const path = pathEl ? (pathEl.textContent || '').trim() : '';
        const base = (window.apiClient && window.apiClient.baseUrl) ? window.apiClient.baseUrl : window.location.origin;
        const exampleRid = reqId && reqId !== '-' ? reqId : 'YOUR-RID';
        const exampleUrl = `${base}${path || ''}`;
        const hintId = id ? `${id}_correlation_help` : '';
        let hint = (hintId && document.getElementById(hintId)) || null;
        if (!hint) {
            hint = document.createElement('div');
            if (hintId) hint.id = hintId;
            hint.className = 'correlation-help';
            hint.style.marginTop = '4px';
            hint.style.color = 'var(--color-text-muted)';
            hint.style.fontSize = '0.8em';
            box.parentNode.insertBefore(hint, box.nextSibling);
        }
        hint.textContent = `Tip: copy X-Request-ID and correlate in server logs; you can also echo headers using: curl -s -I -H 'X-Request-ID: ${exampleRid}' '${exampleUrl}'`;
    }

    /**
     * Show cURL command for request
     */
    showCurl(endpointId, method, path, bodyType) {
        const curlEl = document.getElementById(`${endpointId}_curl`);
        if (!curlEl) return;

        try {
            const { body, query, processedPath } = this.buildRequest(endpointId, path, bodyType);
            const curlCommand = (typeof apiClient.generateCurlV2 === 'function'
                ? apiClient.generateCurlV2(method, processedPath, { body, query })
                : apiClient.generateCurl(method, processedPath, { body, query }));

            curlEl.textContent = curlCommand;
            curlEl.style.display = curlEl.style.display === 'none' ? 'block' : 'none';

            // Show a small note indicating whether the cURL token is masked
            const noteId = `${endpointId}_curl_note`;
            let note = document.getElementById(noteId);
            if (!note) {
                note = document.createElement('div');
                note.id = noteId;
                note.className = 'text-muted';
                note.style.fontSize = '0.85em';
                note.style.margin = '6px 0 0 0';
                curlEl.parentNode.insertBefore(note, curlEl.nextSibling);
            }
            if (apiClient && apiClient.token && !apiClient.includeTokenInCurl) {
                note.textContent = "Note: Token masked in cURL. Use Global Settings toggle to include it, or replace [REDACTED] with your token.";
                note.style.display = 'block';
            } else {
                note.textContent = '';
                note.style.display = 'none';
            }

            // Copy to clipboard button
            if (!curlEl.nextElementSibling || !curlEl.nextElementSibling.classList.contains('copy-curl')) {
                const copyBtn = document.createElement('button');
                copyBtn.className = 'btn btn-sm btn-secondary copy-curl';
                copyBtn.textContent = 'Copy cURL';
                copyBtn.onclick = () => {
                    Utils.copyToClipboard(curlCommand);
                    Toast.success('cURL command copied to clipboard');
                };
                curlEl.parentNode.insertBefore(copyBtn, curlEl.nextSibling);
            }
        } catch (error) {
            curlEl.textContent = `Error generating cURL: ${error.message}`;
            curlEl.style.display = 'block';
        }
    }

    /**
     * Get appropriate button text based on method
     */
    getButtonText(method) {
        const texts = {
            'GET': 'Fetch',
            'POST': 'Submit',
            'PUT': 'Update',
            'DELETE': 'Delete',
            'PATCH': 'Patch'
        };
        return texts[method] || 'Send Request';
    }

    /**
     * Utility: delay function for retries
     */
    delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    /**
     * Batch operation helper
     */
    async executeBatch(operations, options = {}) {
        const {
            parallel = false,
            stopOnError = false,
            onProgress = null
        } = options;

        const results = [];
        const total = operations.length;

        if (parallel) {
            // Execute all operations in parallel
            const promises = operations.map((op, index) =>
                this.executeBatchOperation(op, index, total, onProgress)
            );

            if (stopOnError) {
                return Promise.all(promises);
            } else {
                return Promise.allSettled(promises);
            }
        } else {
            // Execute operations sequentially
            for (let i = 0; i < operations.length; i++) {
                try {
                    const result = await this.executeBatchOperation(operations[i], i, total, onProgress);
                    results.push({ status: 'fulfilled', value: result });
                } catch (error) {
                    results.push({ status: 'rejected', reason: error });
                    if (stopOnError) {
                        throw error;
                    }
                }
            }
            return results;
        }
    }

    /**
     * Execute single batch operation
     */
    async executeBatchOperation(operation, index, total, onProgress) {
        if (onProgress) {
            onProgress({
                current: index + 1,
                total,
                operation,
                status: 'processing'
            });
        }

        const result = await operation.execute();

        if (onProgress) {
            onProgress({
                current: index + 1,
                total,
                operation,
                status: 'completed',
                result
            });
        }

        return result;
    }
}

// Create global instance
const endpointHelper = new EndpointHelper();
try { window.endpointHelper = endpointHelper; } catch (_) {}

// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
    module.exports = EndpointHelper;
}
